Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.

...powered by www.netzwerkartist.de...

 << zurück
Visual C# 2005 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2005

Visual C# 2005
1.320 S., mit 2 CDs, 59,90 Euro
Galileo Computing
ISBN 3-89842-586-X
gp Kapitel 3 Grundlagen der Sprache C#
  gp 3.1 Konsolenanwendungen
    gp 3.1.1 Allgemeine Anmerkungen
    gp 3.1.2 Ein erstes Konsolenprogramm
  gp 3.2 Grundlagen der C#-Syntax
    gp 3.2.1 Kennzeichnen, dass eine Anweisung abgeschlossen ist
    gp 3.2.2 Anweisungs- und Gliederungsblöcke
    gp 3.2.3 Kommentare
    gp 3.2.4 Die Groß- und Kleinschreibung
    gp 3.2.5 Die Struktur einer Konsolenanwendung
  gp 3.3 Variablen und Datentypen
    gp 3.3.1 Variablendeklaration
    gp 3.3.2 Der Variablenbezeichner
    gp 3.3.3 Der Zugriff auf eine Variable
    gp 3.3.4 Ein- und Ausgabemethoden der Klasse »Console«
    gp 3.3.5 Die einfachen Datentypen
    gp 3.3.6 Typkonvertierung
  gp 3.4 Operatoren
    gp 3.4.1 Arithmetische Operatoren
    gp 3.4.2 Vergleichsoperatoren
    gp 3.4.3 Logische Operatoren
    gp 3.4.4 Bitweise Operatoren
    gp 3.4.5 Zuweisungsoperatoren
    gp 3.4.6 Stringverkettung
    gp 3.4.7 Sonstige Operatoren
    gp 3.4.8 Operator-Vorrangregeln
  gp 3.5 Datenfelder (Arrays)
    gp 3.5.1 Die Deklaration und Initialisierung eines Arrays
    gp 3.5.2 Der Zugriff auf die Array-Elemente
    gp 3.5.3 Speicherabbild eines Arrays
    gp 3.5.4 Mehrdimensionale Arrays
    gp 3.5.5 Festlegen der Array-Größe zur Laufzeit
    gp 3.5.6 Bestimmung der Array-Obergrenze
    gp 3.5.7 Die Gesamtanzahl der Array-Elemente
    gp 3.5.8 Verzweigte Arrays
  gp 3.6 Kontrollstrukturen
    gp 3.6.1 Die »if«-Anweisung
    gp 3.6.2 Das »switch«-Statement
  gp 3.7 Programmschleifen
    gp 3.7.1 Die »for«-Schleife
    gp 3.7.2 Die »foreach«-Schleife
    gp 3.7.3 Die »do«- und die »while«-Schleife


Galileo Computing

3.7 Programmschleifen  downtop

Schleifen dienen dazu, Anweisungsfolgen wiederholt auszuführen. Dabei wird zwischen zwei Schleifentypen unterschieden:

gp  bestimmte Schleifen
gp  unbestimmte Schleifen

Ist beim Schleifeneintritt bekannt, wie oft die Anweisungsfolge durchlaufen werden muss, wird von einer bestimmten Schleife gesprochen. Ergibt sich erst während des Schleifendurchlaufs, wann die zyklische Bearbeitung abgebrochen werden kann oder muss, spricht man von unbestimmten Schleifen. Die Grenzen zwischen diesen beiden Typen sind dabei nicht eindeutig, sondern können durchaus verwischen. Eine bestimmte Schleife kann wie eine unbestimmte agieren, eine unbestimmte wie eine bestimmte.


Galileo Computing

3.7.1 Die »for«-Schleife  downtop

Man setzt eine for-Schleife zumeist dann ein, wenn bekannt ist, wie oft bestimmte Anweisungen ausgeführt werden müssen. Die allgemeine Syntax des for-Schleifenkonstrukts sieht dabei wie folgt aus:


for(Ausdruck1; Ausdruck2; Ausdruck3)
{
   // Anweisungen
}

Die for-Schleife setzt sich aus zwei Komponenten zusammen: aus dem Schleifenkopf, der die Eigenschaft der Schleife beschreibt, und aus dem sich daran anschließenden Schleifenblock in geschweiften Klammern, der die wiederholt auszuführenden Anweisungen enthält. Handelt es sich dabei nur um eine Anweisung, kann auf die geschweiften Klammern verzichtet werden.

Um die Anzahl der Durchläufe einer for-Schleife festzulegen, bedarf es eines Schleifenzählers, dessen Anfangswert durch Ausdruck1 beschrieben wird. Der Endwert wird im Ausdruck2 festgelegt und im Ausdruck3 schließlich, auf welchen Betrag der Schleifenzähler bei jedem Schleifendurchlauf erhöht werden soll. Dazu ein Beispiel:


for(int counter = 0; counter < 10; counter++) {
  Console.WriteLine("Zählerstand = {0}",counter);
}

Der Schleifenzähler heißt hier counter. Sein Startwert beträgt 0, und er wird bei jedem Schleifendurchlauf um den Wert 1 erhöht. Erreicht counter den Wert 10, wird das Programm mit der Anweisung fortgesetzt, die dem Anweisungsblock der Schleife folgt.

Führen wir den Code aus, werden wir an der Konsole die folgende Ausgabe erhalten:


Zählerstand = 0
Zählerstand = 1
Zählerstand = 2
Zählerstand = 3
...
Zählerstand = 8
Zählerstand = 9

Weil der Schleifenblock nur eine Anweisung enthält, könnte die for-Schleife auch wie folgt codiert werden:


for(int counter = 0; counter < 10; counter++)
  Console.WriteLine("Zählerstand = {0}",counter);

Die Arbeitsweise der »for«-Schleife

Stößt der Programmablauf auf eine for-Schleife, wird zuerst Ausdruck1 – auch Initialisierungsausdruck genannt – ausgewertet. Dieser initialisiert den Zähler der Schleife mit einem Startwert. Der Zähler der Schleife in unserem Beispiel wird mit dem Startwert 0 initialisiert.

Ausdruck2, der Bedingungsausdruck, wertet vor jedem Schleifendurchlauf den aktuellen Stand des Zählers aus. Im Beispiel von oben lautet die Bedingung:


counter < 10

Der Bedingungsausdruck kann unter Einbeziehung der diversen Operatoren beliebig komplex werden, muss aber immer ein boolesches Ergebnis haben. Der Anweisungsblock wird nur dann ausgeführt, wenn Ausdruck2 true ist, ansonsten setzt das Programm seine Ausführung mit der Anweisung fort, die dem Schleifenblock folgt.

Ausdruck3 (Reinitialisierungsausdruck) übernimmt die Steuerung des Schleifenzählers. Er wird dazu benutzt, den Schleifenzähler entweder zu inkrementieren oder zu dekrementieren. In unserem Fall wird der Zähler jeweils um +1 erhöht. Die Erhöhung erfolgt immer dann, wenn der Anweisungsblock der Schleife durchlaufen ist. Danach bewertet der Bedingungsausdruck den neuen Zählerstand.

Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 3.14   Das Ablaufdiagramm der »for«-Schleife

Die Zählervariable

Grundsätzlich gibt es zwei Möglichkeiten, die Zählervariable zu deklarieren, die für das Abbruchkriterium herangezogen wird:

gp  innerhalb des Schleifenkopfs
gp  vor der Schleife

Welcher Notation Sie den Vorzug geben, hängt davon ab, über welche Sichtbarkeit der Zähler verfügen soll. Betrachten Sie dazu zunächst das folgende Codefragment:


static void Main(string[] args) {
  for(int i = 0; i <= 10 ;i++) {
    Console.WriteLine("Zählerstand = {0}",i);
  }
  // die folgende Codezeile verursacht einen Kompilierfehler
  Console.WriteLine(i);
}

Eine Zählervariable, die im Schleifenkopf deklariert wird, gilt als lokale Variable der Schleife und ist deshalb auch nur innerhalb des Anweisungsblocks der for-Schleife gültig. Der Zugriff auf den Zähler von außerhalb der Schleife führt deshalb auch zu einem Kompilierfehler.

Implementieren Sie innerhalb einer Prozedur wie Main mehrere Schleifen, müssen Sie daher auch jedes Mal von neuem den Zähler deklarieren:


static void Main(string[] args) {
  for(int i = 0; i <= 10 ;i++) {}
  ...
  for(int i = 12; i <= 100 ;i += 3){}
}

Die bessere Lösung wäre in diesem Fall die Deklaration der Zählervariablen vor dem Auftreten der ersten Schleife:


int i;
for(i = 0; i <= 10 ;i++) {/*...*/}
for(i = 12; i <= 100 ;i += 3){/*...*/}

An diesem Punkt angekommen stellt sich jetzt die Frage, ob beim Vorliegen einer einzigen for-Schleife die gleichzeitige Deklaration und Initialisierung im Schleifenkopf der vorgezogenen Deklaration der Zählervariablen vor den Schleifenkopf vorzuziehen ist. Eine klare Antwort darauf gibt es nicht. Der besseren Übersicht wegen scheint es jedoch vorteilhaft zu sein, die Deklaration im Initialisierungsausdruck vorzunehmen.

»for«-Schleifen mit beliebiger Veränderung des Zählers

In den meisten Fällen erfüllt eine ganzzahlige Schrittweite vollkommen die Anforderungen. Das muss aber nicht immer so sein, manchmal werden auch kleinere Schrittweiten benötigt, also im Bereich von Fließkommazahlen. Wie Sie jedoch bereits wissen, sind Fließkommazahlen systembedingt ungenau. Das kann bei Schleifen besonders fatale Folgen haben. Sehen Sie sich dazu das folgende Codefragment an:


static void Main(string[] args) {
  int i = 0;
  for(double counter = 0; counter <= 2 ;counter += 0.1) {
    i++;
    Console.WriteLine("{0}. Zählerstand = {1}", i, counter);
  }
  Console.ReadLine();
}

Normalerweise würden wir auf den ersten Blick keinen Haken vermuten – erst wenn wir das Programm ausführen, werden wir feststellen, dass der letzte Zählerwert fehlt:


1. Zählerstand = 0
2. Zählerstand = 0,1
...
18. Zählerstand = 1,7
19. Zählerstand = 1,8
20. Zählerstand = 1,9

Die systembedingte Ungenauigkeit der Fließkommazahlen bewirkt, dass der Zählerstand im letzten Schritt nicht exakt 2 ist, sondern ein wenig größer. Damit wird der zweite Ausdruck des Schleifenkopfs zu false und bewirkt den vorzeitigen Ausstieg aus der Schleife – der letzte erforderliche Schleifendurchlauf wird überhaupt nicht ausgeführt.

Diese These lässt sich beweisen, wenn die Anweisung zur Ausgabe an der Konsole durch die folgende ersetzt wird:


Console.WriteLine("{0}. Zählerstand = {1:E16}", i, counter);

Wir erzwingen nun die Ausgabe in Exponentialschreibweise und geben eine Genauigkeit von 16 Nachkommastellen an – denn bekanntermaßen wird der Typ double an der 16. Nachkommastelle ungenau. Die Ausgabe an der Konsole sieht dann wie in der folgenden Abbildung gezeigt aus.

Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 3.15   Fließkommazahl als Zähler – die Ausgabe an der Konsole

Dieser Konflikt kann vermieden werden, wenn sowohl der Zähler als auch die Schrittweite ganzzahlig gemacht werden. In unserem Beispiel wird mit dem Faktor 10 die Schrittweite auf +1 gesetzt. Analog muss auch die Ausstiegsbedingung angepasst werden. Um den Effekt bei der Ausgabe wieder rückgängig zu machen, dividieren wir das auszugebende Datum am Ende um denselben Faktor.


static void Main(string[] args) {
  int i = 0;
  for(double counter = 0; counter <= 20 ;counter++) {
    i++;
    Console.WriteLine("{0}. Zählerstand = {1}",i, counter/10);
  }
  Console.ReadLine();
}

Natürlich bewirkt die Division ihrerseits auch wieder eine Ungenauigkeit, aber das liegt in der Natur der Fließkommazahlen, und wir müssen es akzeptieren. Andererseits haben wir aber die Gewissheit, dass zumindest die Anzahl der Schleifendurchläufe korrekt ist.

Die Initialisierung von Arrays in einer »for«-Schleife

Sie haben gesehen, dass mit for-Schleifen Anweisungssequenzen wiederholt ausgeführt werden. Dieser Schleifentyp eignet sich daher insbesondere dazu, Array-Elemente mit bestimmten Werten zu initialisieren – vorausgesetzt, zwischen den einzelnen Werten liegt eine mathematische oder logische Beziehung vor.

Machen wir uns das an einem einfachen Beispiel deutlich. Das Array myArr soll mit Zahlen initialisiert werden, die dem Quadrat des Index des Elements entsprechen. Den höchsten vertretenen Index soll der Anwender an der Konsole eingeben. Der Code dazu sieht wie folgt aus:


static void Main(string[] args) {
  int[] myArr;
  Console.Write("Geben Sie den höchsten Array-Index ein: ");
  myArr = new int[Convert.ToInt32(Console.ReadLine()) + 1];
  for(int i = 0; i < myArr.Length; i++) {
    myArr[i] = i * i;
    Console.WriteLine(myArr[i]);
  }
  Console.ReadLine();
}

Nach der Deklaration des Arrays und der sich anschließenden Aufforderung, die Größe des Arrays festzulegen, wird das Array entsprechend der Eingabe des Anwenders initialisiert. Die Anweisung dazu erscheint im ersten Moment verhältnismäßig komplex, ist aber recht einfach zu interpretieren. Dabei geht man – genauso wie es auch die Laufzeitumgebung macht – von der innersten Klammerebene aus, im vorliegenden Fall der Entgegennahme der Benutzereingabe:


Console.ReadLine()

Die Eingabe des Anwenders ist eine Zeichenfolge, also vom Typ string. Da die Indexangabe eines Arrays immer ein int sein muss, sind wir zu einer Konvertierung gezwungen:


Convert.ToInt32(Console.ReadLine())

Jetzt gilt es noch zu bedenken, dass per Vorgabe die Eingabe den höchsten Index des Arrays darstellt, wir aber bei einer Array-Initialisierung immer die Anzahl der Elemente angeben. Um unser Array endgültig richtig zu dimensionieren, muss die konvertierte Benutzereingabe noch um 1 erhöht werden, also:


Convert.ToInt32(Console.ReadLine()) + 1

Mit der daraus resultierenden Zahl kann das Array nun endgültig in der vom Anwender gewünschten Kapazität initialisiert werden.

Jetzt folgt die for-Schleife. Da wir jedem Array-Element im Schleifenblock das Quadrat seines Index zuweisen wollen, lassen wir den Schleifenzähler über alle vertretenen Indizes laufen – also von 0 bis zum höchsten Index. Letzteren ermitteln wir aus der Eigenschaft Length unseres Arrays, die uns die Gesamtanzahl der Elemente liefert. Diese ist immer um 1 höher als der letzte Index im Array. Daher entspricht die Bedingung


i < myArr.Length

immer den Forderungen, denn die Schleife wird jetzt so lange durchlaufen, bis die Integerzahl erreicht ist, die kleiner ist als die Anzahl der Elemente. Gleichwertig könnten wir auch formulieren:


i <= myArr.Length – 1

Der Schleifenkopf ist nun anforderungsgerecht formuliert, die Anweisungen des Schleifenblocks werden genauso oft durchlaufen, wie das Array Elemente aufweist. Da bei jedem Schleifendurchlauf der Schleifenzähler ein Pendant in Form eines Array-Index aufweist, können wir den Zähler dazu benutzen, jedes einzelne Array-Element anzusprechen:


myArr[i] = i * i;

Beim ersten Durchlauf mit i = 0 wird demnach myArr[0] die Zahl 0 zugewiesen, beim zweiten Durchlauf mit i = 1 dem Element myArr[1] der Wert 1 usw.

Die Argumente der »Main«-Prozedur

Bisher haben wir unsere Programme immer nur durch einen einfachen Aufruf gestartet, entweder direkt aus der Entwicklungsumgebung heraus oder durch die Angabe des Dateinamens an der Eingabekonsole. Verteilen wir eine Anwendung, wird ein Anwender jedoch niemals aus der Entwicklungsumgebung heraus die Applikation starten, sondern entweder durch Doppelklick auf die ausführbare EXE-Datei im Explorer, durch die Eingabe des Namens der ausführbaren Datei an der Eingabekonsole oder über die Option Start · Ausführen...

Die beiden letztgenannten Punkte eröffnen noch weitere Möglichkeiten: Es können der Main-Methode auch Befehlszeilenparameter als zusätzliche Informationen übergeben werden, die im Array args der Parameterliste der Main-Methode entgegengenommen werden:


static void Main(string[] args)

Nehmen wir an, wir würden eine Anwendung namens MyApplication.exe an der Konsole wie folgt starten:


MyApplication Peter Willi Udo

Die drei Übergabeparameter »Peter«, »Willi« und »Udo« werden von Main im string-Array args empfangen und können von der Anwendung für weitere Operationen benutzt werden. Da das Programm zur Laufzeit jedoch nicht weiß, ob und wie viele Parameter übergeben worden sind, wird das Array args zunächst dahingehend abgefragt, ob überhaupt ein gültiges Element enthalten ist. Wenn die Anzahl der Elemente größer 0 ist, kann mit einer for-Schleife in bekannter Weise auf jedes Array-Element zugegriffen werden. Sehen wir uns das an einem konkreten Beispiel an:


// --------------------------------------------------------------
// Beispiel: ...\Kapitel 3\Befehlszeilenparameter
// --------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
namespace Befehlszeilenparameter {
  class Program {
    static void Main(string[] args) {
      // prüfen, ob beim Programmaufruf eine oder mehrere
      // Zeichenfolgen übergeben worden sind
      if(args.Length > 0) {
        // die Zeichenfolgen an der Konsole anzeigen
        for(int i = 0; i < args.Length; i++)
          Console.WriteLine(args[i]);
        }
      else
        Console.WriteLine("Kein Übergabestring");
      Console.ReadLine();
    }
  }
}

Das if-Statement stellt unter Auswertung der Length-Eigenschaft auf args fest, ob das Array leer ist oder nicht. Hat der Anwender zumindest einen Parameter übergeben, wird die for-Schleife ausgeführt, die den Inhalt des Parameters an der Konsole ausgibt.

Grundsätzlich werden alle übergebenen Parameter als Zeichenfolgen empfangen. Das soll uns aber nicht davon abhalten, im Bedarfsfall der Laufzeit auch Zahlen zu übergeben. Allerdings dürfen wir dann nicht vergessen, mit einer der Methoden der Klasse Convert die Zeichenfolge in den erforderlichen Datentyp zu konvertieren.

Verschachtelte Schleifen

for-Schleifen können praktisch beliebig verschachtelt werden. Im nächsten Beispiel wird gezeigt, wie eine verschachtelte Schleife dazu benutzt werden kann, einen Baum beliebiger Größe – hier durch Buchstaben dargestellt – an der Konsole auszugeben.


1:      M
2:     MMM
3:    MMMMM
4:   MMMMMMM
5:  MMMMMMMMM
6: MMMMMMMMMMM

Jede Ausgabezeile setzt sich aus einer Anzahl von Leerzeichen und Buchstaben zusammen und hängt von der Größe der Darstellung ab. Für die Leerzeichen gilt:

Anzahl Leerzeichen = Gesamtanzahl der Zeilen – aktuelle Zeilennummer

Die auszugebenden Buchstaben folgen der Beziehung:

Anzahl der Buchstaben = aktuelle Zeilennummer * 2 – 1

Um die gewünschte Ausgabe zu erhalten, wird in einer äußeren for-Schleife jede Stufe (Zeile) des Baums separat behandelt. Darin eingebettet sind zwei weitere Schleifen implementiert, die jede für sich zuerst vollständig ausgeführt wird – wir haben es also mit zwei parallelen inneren Schleifen zu tun. Dabei werden in der ersten inneren Schleife zuerst die Leerzeichen geschrieben, in der zweiten die Buchstaben. Die Struktur der Schleifen sieht demnach wie folgt aus:


// äußere Schleife beschreibt bei jedem Durchlauf eine Zeile
for(...) {
  // Leerzeichen schreiben
  for(...) {/*...*/}
   // Buchstaben schreiben
  for(...) {/*...*/}
}

Sehen wir uns nun den Programmcode an, der den gestellten Anforderungen genügt. Das Programm verlangt, dass der Anwender die Anzahl der Stufen als Befehlszeilenparameter angibt. Unterlässt er dies, wird das Programm mit einem entsprechenden Hinweis beendet.


// --------------------------------------------------------------
// Beispiel: ... \Kapitel 3\Baumstruktur
// --------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
namespace Baumstruktur {
  class Program {
    static void Main(string[] args) {
      // prüfen, ob das Array args leer ist
      if(args.Length == 0) {
        Console.Write("Geben Sie beim Start der Anwendung ");
        Console.Write("einen Parameter an.");
      }
      else {
        // das erste Element in den Typ int konvertieren
        int zeile = Convert.ToInt32(args[0]);
        // jede Stufe des Buchstabenbaum aufbauen
        for(int i = 1; i <= zeile; i++) {
          // Leerzeichen schreiben
          for(int j = 1; j <= zeile – i; j++)
            Console.Write(" ");
            // Buchstaben schreiben
            for(int j = 1; j <= i * 2 – 1; j++)
              Console.Write("M");
            Console.WriteLine();
        }
      }
      Console.ReadLine();
    }
  }
}

Vorzeitiges Beenden einer Schleife mit »break«

Situationsbedingt kann es sich zur Laufzeit als erforderlich erweisen, nicht auf das Erfüllen der Abbruchbedingung zu warten, sondern den Schleifendurchlauf vorzeitig zu beenden. C# stellt dazu ein Schlüsselwort zur Verfügung, das uns in diese Lage versetzt: break.


for(int i = 0; i <= 10; i++) {
  if(i == 3)
    break;
  Console.WriteLine("Zähler = {0}", i);
}

Dieses Codefragment wird zu der folgenden Ausgabe an der Konsole führen:


Zähler = 0
Zähler = 1
Zähler = 2

break beendet die Schleife unabhängig von der im Schleifenkopf formulierten Abbruchbedingung und setzt den Programmablauf hinter dem Anweisungsblock der for-Schleife fort.

Sie können break auch in einer verschachtelten Schleife einsetzen. Das wirkt sich nur auf die for-Schleife aus, in deren direktem Anweisungsblock der Abbruch codiert ist, die äußeren Schleifen zeigen sich unbeeindruckt. Im nachstehenden Code wird der Abbruch herbeigeführt, wenn der Zähler inner den Wert 2 aufweist. Die innere Schleife wird danach als beendet betrachtet und das Programm mit der Anweisung fortgesetzt, die dem Anweisungsblock der inneren Schleife folgt.


for(int outer=0; outer<=10; outer++) {
  Console.WriteLine("outer = {0}",outer);
  for(int inner = 0; inner <= 10; inner++) {
    Console.WriteLine("inner = {0}",inner);
    if(inner == 2)
      break;
  }
}

Die Ausgabe lautet:


outer = 0
inner = 0
inner = 1
inner = 2
outer = 1
inner = 0
inner = 1
...

Abbruch der Anweisungen im Schleifenblock mit »continue«

Sehr ähnlich wie break verhält sich auch die Anweisung continue. Die Bearbeitung des Codes in der Schleife wird zwar abgebrochen, aber die Steuerung wieder an den Schleifenkopf übergeben. Mit anderen Worten: Alle Anweisungen, die zwischen continue und dem Ende des Anweisungsblocks stehen, werden übersprungen. Das wollen wir uns ebenfalls an einem Codefragment ansehen:


for(int i = 0; i <= 10; i++) {
  if(i == 3)
    continue;
  Console.WriteLine("Zähler = {0}", i);
}

Die Ausgabe an der Konsole sieht wie folgt aus:


Zähler = 0
Zähler = 1
Zähler = 2
Zähler = 4
Zähler = 5
...

Steht der Zähler auf 3, ist die Bedingung erfüllt. Es wird continue ausgeführt mit der Folge, dass die Laufzeit die folgende Ausgabeanweisung überspringt und die Schleife mit dem Zählerstand 4 fortgesetzt wird.


break beendet die Schleife und setzt den Programmablauf hinter dem Anweisungsblock der for-Schleife fort. Mit continue wird die Abarbeitung der Anweisungen im Schleifenblock abgebrochen und die Kontrolle zur Fortsetzung des Programms an den Schleifenkopf übergeben.

Die Ausdrücke der »for«-Schleife

Zum Abschluss der Ausführungen über die Möglichkeiten der for-Schleife unter C# kommen wir noch einmal auf die drei Ausdrücke im Schleifenkopf zurück. Was bisher noch nicht erwähnt worden ist, sei an dieser Stelle nachgeholt: Alle drei sind optional, müssen also nicht angegeben werden. Fehlt aber ein Ausdruck, gilt dieser stets als »erfüllt«. Im Extremfall lässt sich eine Schleife sogar ganz ohne explizit ausformulierten Schleifenkopf konstruieren. Wir erhalten dann die kürzeste for-Schleife überhaupt – allerdings handelt es sich dann auch um eine Endlosschleife, da das Abbruchkriterium in dem Sinne als erfüllt gilt, dass die Schleife nicht beendet werden soll:


// Endlosschleife for(;;);


Galileo Computing

3.7.2 Die »foreach«-Schleife  downtop

Die for-Schleife setzt drei Ausdrücke voraus, die erst in Kombination die gewünschte Iteration ermöglichen. C# kennt noch ein weiteres Konstrukt, um ein Array vom ersten bis zum letzten Element zu durchlaufen: die foreach-Schleife. Sehen wir uns dazu ein Beispiel an, das genauso wie das oben gezeigte operiert:


int[] intArr = {2,4,6,8};
foreach(int tempElement in intArr) {
  Console.WriteLine(tempElement);
}

Anstatt jedes Element über seinen Index anzusprechen, wird nun das Array als eine Einheit angesehen, gebildet aus mehreren typgleichen Elementen. Das Array wird vom ersten bis zum letzten Mitglied durchlaufen, wobei die Adressierung nun über eine Laufvariable als temporäres Element, hier als tempElement bezeichnet, erfolgt. Bei der Iteration wird tempElement jedes Mal auf ein anderes Array-Element verweisen. Daher ist die Indexangabe auch überflüssig.

Die allgemeine Syntax der foreach-Schleife lautet:


// Syntax: foreach-Schleife
foreach(Datentyp Bezeichner in Array-Bezeichner) {/*...*/} 

Beachten Sie, dass die Deklaration der Laufvariablen in den Klammern nicht optional, sondern unabdingbar ist. Daher führt das folgende Codefragment zu einem Fehler:


int tempElement;
// Fehler im foreach-Statement
foreach(tempElement in intArr) {/*...*/}

Durchlaufen Sie ein Array von Elementen eines einfachen Datentypen, sind die Daten schreibgeschützt, können also nicht verändert werden, z.B.:


int[] intArr = {1,2,3,4,5};
foreach(int temp in intArr)
   temp = 33;  // FEHLER !!

An dieser Stelle muss aber schon darauf hingewiesen werden, dass das Array nur schreibgeschützt ist, wenn es Wertetypen beschreibt. Ein Array von Objekten basierend auf Referenztypen verhält sich anders: Die Objektdaten können innerhalb einer foreach-Schleife manipuliert werden. Wie das praktisch aussieht, werden Sie ab Kapitel 4 erfahren.


Galileo Computing

3.7.3 Die »do«- und die »while«-Schleife  toptop

Ist die Anzahl der Iterationen bereits beim Eintritt in die Schleife bekannt, wird zumeist das for-Schleifenkonstrukt verwendet. Ergibt sich jedoch erst zur Laufzeit der Anwendung, wie oft der Schleifenkörper durchlaufen werden muss, bietet sich eher die do- oder die while-Schleife an. Grundsätzlich können jedoch alle auftretenden Anforderungen an wiederholt auszuführende Anweisungen mit einem dieser beiden Typen formuliert werden, sie können also die for-Schleife durchaus gleichwertig ersetzen.

Die »while«-Schleife

In eine Schleife wird dann eingetreten, wenn bestimmte Bedingungen erfüllt sind. Bei der for-Schleife wird diese Bedingung durch den Schleifenzähler festgelegt, bei einer while-Schleife wird die Bedingung hinter dem Schlüsselwort while in runden Klammern angegeben. Da sich die Anweisungen der Bedingungsprüfung anschließen, spricht man auch von einer kopfgesteuerten Schleife. Sehen wir uns daher zunächst die Syntax dieses Schleifentyps an:


// Syntax: while-Schleife
while(Bedingung) 
{
  // Anweisungen
}

Bei der Bedingung handelt es sich um einen booleschen Ausdruck, der aus den Vergleichsoperatoren gebildet wird und entweder true oder false liefert.


Eine while-Schleife wird ausgeführt, solange die Bedingung wahr, also true ist. Die Schleife wird beendet, wenn die Bedingung false ist.

Ist die Bedingung schon bei der ersten Überprüfung falsch, werden die Anweisungen im Schleifenkörper überhaupt nicht ausgeführt.

Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 3.16   Ablaufdiagramm der »while«-Schleife

Da im Gegensatz zur for-Schleife die Bedingung zum Austritt aus der while-Schleife nicht automatisch verändert wird, muss innerhalb des Schleifenkörpers eine Anweisung stehen, die das Verlassen der Schleife zu einem vordefinierten Zeitpunkt ermöglicht. Vergessen Sie eine solche Anweisung, liegt der klassische Fall einer Endlosschleife vor.


Hinweis   Wenn Sie beim Testen eines Programms aus der Entwicklungsumgebung heraus in eine Endlosschleife geraten, können Sie mit der Tastenkombination (Strg)+(Pause) die Laufzeit unterbrechen und wieder zur Entwicklungsumgebung zurückkehren.

Im folgenden Beispiel muss der Anwender zur Laufzeit eine Zahl angeben, mit der er die Anzahl der Schleifendurchläufe festlegt. Die zusätzliche Zählervariable counter dient als Hilfsvariable, um die Austrittsbedingung zu formulieren. Sie wird innerhalb der Schleife bei jedem Schleifendurchlauf um 1 erhöht und bewirkt, dass die while-Schleife zum gewünschten Zeitpunkt verlassen wird.


// --------------------------------------------------------------
// Beispiel: ...\Kapitel 3\WhileDemo
// --------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
namespace WhileDemo {
  class Program {
    static void Main(string[] args) {
      Console.Write("Geben Sie eine Zahl zwischen\n");
      Console.Write("0 und einschließlich 10 ein: ");
      int zahl = Convert.ToInt32(Console.ReadLine());
      int counter = 1;
      while(counter <= zahl) {
        Console.WriteLine("{0}.Schleifendurchlauf",counter);
        // Änderung der Austrittsbedingung
        counter++;
      }
      Console.ReadLine();
    }
  }
}

Genauso wie eine for-Schleife kann auch eine while-Schleife entweder mit break oder mit continue unterbrochen werden. Die Auswirkungen sind bekannt:

gp  Mit break wird die gesamte Schleife als beendet angesehen. Das Programm setzt seine Ausführung mit der Anweisung fort, die dem Anweisungsblock der Schleife folgt.
gp  Mit continue wird der aktuelle Iterationsvorgang abgebrochen. Anweisungen, die innerhalb des Schleifenblocks auf continue folgen, werden nicht mehr ausgeführt. Die Steuerung wird an die Schleife zurückgegeben.

Daher würde


int intVar = 0;
while(intVar < 5) {
  intVar++;
  if(intVar == 3)
    break;
  Console.WriteLine(intVar);
}

die Ausgabe


1
2

haben, während der Austausch von break gegen continue die Zahlenwerte


1
2
4
5

ausgibt.

Die »do«-Schleife

Die do-Schleife unterscheidet sich dahingehend von der while-Schleife, dass die Schleifenbedingung am Ende der Schleife ausgewertet wird. Die do-Schleife ist eine fußgesteuerte Schleife. Die Folge ist, dass die Anweisungen innerhalb des Anweisungsblocks zumindest einmal durchlaufen werden.


// Syntax: do-Schleife
do
{
  // Anweisungen
} while(Bedingung);

Der Anweisungsblock wird so lange wiederholt ausgeführt, bis die Bedingung false ist. Danach wird mit der Anweisung fortgefahren, die sich unmittelbar anschließt.

Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 3.17   Ablaufdiagramm der do-Schleife

Die Tatsache, dass die Laufzeit einer Anwendung mindestens einmal in den Anweisungsblock der do-Schleife eintaucht, kann man sich zunutze machen, wenn eine bestimmte Eingabe vom Anwender erforderlich wird. Ist die Eingabe unzulässig, wird eine Schleife so lange durchlaufen, bis sich der Anwender »überzeugen« lässt. Im folgenden Beispiel wird das demonstriert.

 

 


// --------------------------------------------------------------
// Beispiel: ...\Kapitel 3\DoDemo
// --------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
namespace DoDemo {
  class Program {
    static void Main(string[] args) {
      // Informationsanzeige
     Console.Write("W – Programm fortsetzen\n");
     Console.Write("E – Programm beenden\n");
     Console.Write("-----------------------\n");
      // Schleife wird so oft durchlaufen, bis der Anwender
      // eine gültige Eingabe macht
      do {
        Console.Write("Ihre Wahl: ");
        string eingabe = Console.ReadLine();
        if(eingabe == "W")
          // das Programm nach dem Schleifenende fortsetzen
          break;
        else if(eingabe == "E")
          // das Programm beenden
          return;
        else {
          // Fehleingabe! 
          Console.Write("Falsche Eingabe – ");
          Console.Write("Neueingabe erforderlich\n");
          Console.Write("-----------------------\n");
        }
      } while(true);
      Console.WriteLine("...es geht weiter.");
      Console.ReadLine();
    }
  }
}

Zugelassen sind nur die beiden Eingaben »W« und »E«. Jede andere Eingabe führt zu einer erneuten Iteration. Die do-Schleife ist wegen ihrer Austrittsbedingung


while(true)

als Endlosschleife konstruiert, aus der es ein kontrolliertes Beenden nur mit der Sprunganweisung break gibt, wenn der Anwender mit der Eingabe »W« eine Fortsetzung des Programms wünscht.

Mit der Anweisung return wird das laufende Programm vorzeitig beendet. Diese Anweisung dient per Definition dazu, die aktuell ausgeführte Methode zu verlassen. Handelt es sich dabei aber um die Main-Methode einer Konsolenanwendung, kommt das dem Beenden der Anwendung gleich. In Kapitel 4 werden Sie die return-Anweisung im Zusammenhang mit den Klassenmethoden noch oft benutzen.

 << zurück
  
  Zum Katalog
Zum Katalog: Visual C# 2005
Visual C# 2005
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Fortgeschrittene Programmierung mit Visual C# 2005






 Fortgeschrittene
 Programmierung
 mit Visual C# 2005


Zum Katalog: Einstieg in Visual C# 2005






 Einstieg in
 Visual C# 2005


Zum Katalog: Einstieg in Visual Basic 2005






 Einstieg in
 Visual Basic 2005


Zum Katalog: Visual Basic 2005






 Visual Basic 2005


Zum Katalog: Java ist auch eine Insel






 Java ist auch eine
 Insel


Zum Katalog: Konzepte und Lösungen für Microsoft-Netzwerke






 Konzepte und
 Lösungen für
 Microsoft-Netzwerke


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo








Copyright © Galileo Press 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de